Skip to content

feat: Add MCP parser plugin and body access infrastructure#361

Merged
huang195 merged 4 commits into
rossoctl:mainfrom
huang195:feat/mcp-parser-body-access
May 3, 2026
Merged

feat: Add MCP parser plugin and body access infrastructure#361
huang195 merged 4 commits into
rossoctl:mainfrom
huang195:feat/mcp-parser-body-access

Conversation

@huang195

@huang195 huang195 commented May 3, 2026

Copy link
Copy Markdown
Member

Summary

  • Add mcp-parser plugin: parses MCP JSON-RPC requests (tools/call, resources/read, prompts/get) and populates pctx.Extensions.MCP for downstream policy plugins
  • Add body access infrastructure across all listener modes:
    • Pipeline.NeedsBody() introspects plugin capabilities
    • ext_proc: two-phase processing via ModeOverride with ProcessingMode_BUFFERED
    • forward/reverse proxy: conditional io.ReadAll before pipeline execution
    • Startup validation rejects body-access plugins in waypoint mode (ext_authz limitation)
  • Add pipeline-level logging: plugin rejections (Info), plugin completion (Debug), context cancellation
  • Add structured logging in mcp-parser and all listener body-buffering paths

The mcp-parser is registered but NOT added to default pipelines — users opt in via YAML config. This prevents body buffering overhead for deployments that don't need MCP awareness.

Context

This is the first Phase 2 deliverable from the plugin architecture proposal. PRs #358 and #359 established the pipeline with jwt-validation and token-exchange. This PR adds the first plugin that couldn't exist without body access.

The MCP parser enables future plugins (tool-policy, audit, guardrails) to make authorization decisions at the MCP tool level — the core value proposition of the plugin architecture.

Test plan

  • 10 unit tests for mcp-parser (all branches: tool call, resource read, prompt get, unknown method, nil/empty body, invalid JSON, missing params, capabilities, OnResponse)
  • 3 body-buffering tests for ext_proc (inbound body deferral, outbound body deferral, no buffering when not needed)
  • 2 body-buffering tests for forward proxy
  • 2 body-buffering tests for reverse proxy
  • Full test suite passes: go test ./authlib/... ./cmd/authbridge/...
  • go vet clean
  • End-to-end: configure mcp-parser in Kind cluster, verify MCP tool calls are parsed

Assisted-By: Claude (Anthropic AI) noreply@anthropic.com

Add the mcp-parser plugin -- the first plugin that requires body access.
It parses MCP JSON-RPC requests (tools/call, resources/read, prompts/get)
and populates pctx.Extensions.MCP for downstream policy plugins.

Body access infrastructure:
- Pipeline.NeedsBody() checks if any plugin declares BodyAccess
- ext_proc: two-phase processing via ModeOverride BUFFERED
- forward/reverse proxy: conditional io.ReadAll before pipeline run
- Startup validation rejects body-access plugins in waypoint mode

The mcp-parser is registered but NOT in default pipelines -- users opt in
via config. This prevents body buffering overhead for deployments that
do not need MCP awareness.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Hai Huang <huang195@gmail.com>
huang195 added 2 commits May 3, 2026 14:25
Prevent OOM from oversized request bodies when body-access plugins are
configured. Uses http.MaxBytesReader with a 1MB limit matching Envoy's
default per_stream_buffer_limit_bytes. Returns 413 for oversized bodies.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Hai Huang <huang195@gmail.com>
Step-by-step instructions for enabling mcp-parser via the
authbridge-runtime-config ConfigMap, including log verification
and troubleshooting.

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Hai Huang <huang195@gmail.com>
"github.com/kagenti/kagenti-extensions/authbridge/authlib/pipeline"
)

type MCPParser struct{}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use a descriptive comment?

Comment thread authbridge/demos/mcp-parser/README.md Outdated
## Prerequisites

- A running Kagenti cluster (Kind or OpenShift) with the Ansible installer
completed

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it matter what kind of Kubernetes cluster, and how Kagenti was installed?

Comment thread authbridge/demos/mcp-parser/README.md Outdated
Comment on lines +10 to +11
completed
- An agent namespace (e.g., `team1`) with AuthBridge sidecars injected

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
completed
- An agent namespace (e.g., `team1`) with AuthBridge sidecars injected
completed
- A namespace (e.g., `team1`) with labeled with `kagenti-enabled: "true"` for AuthBridge sidecar injection.

@esnible esnible left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-structured PR. The NeedsBody() introspection avoids buffering overhead when no plugin needs it, ext_proc uses ModeOverride to dynamically request bodies from Envoy, and forward/reverse proxy paths use MaxBytesReader for OOM protection. Waypoint startup validation is a good safety net. Test coverage is thorough — 10 unit tests for the parser, 7 body-buffering integration tests across all 3 listener modes.

Areas reviewed: Go (plugin, pipeline, all 3 listeners, main), Tests, Docs, Security
Commits: 3/3 signed-off
CI: 16/16 passing

}

func (p *bodyRecorderPlugin) Name() string { return "body-recorder" }
func (p *bodyRecorderPlugin) Capabilities() pipeline.PluginCapabilities {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: bodyRecorderPlugin is defined identically in 3 test files (extproc, forwardproxy, reverseproxy). Consider extracting to a shared testutil package to avoid triple-maintenance. Not blocking since the type is small.

"net/url"

"github.com/kagenti/kagenti-extensions/authbridge/authlib/pipeline"
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: maxBodySize is defined independently in both forwardproxy and reverseproxy. If the limit ever changes, they could drift. Consider a shared constant (e.g., in pipeline or a listener/common package). Low priority since there are only two today.

if p.NeedsBody() {
slog.Debug("ext_proc: requesting body from Envoy", "direction", direction)
pendingHeaders = headers
pendingDirection = direction

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: The ext_proc path relies on Envoy's per_stream_buffer_limit_bytes (default 1MB) to bound body size, unlike the forward/reverse proxy paths which enforce MaxBytesReader explicitly. If an operator changes Envoy's buffer limit, ext_proc silently accepts larger bodies. Consider documenting this assumption or adding a size check after receiving RequestBody.

ctx := stream.Context()

var pendingHeaders *corev3.HeaderMap
var pendingDirection string

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: pendingHeaders/pendingDirection are scoped to the entire Process function but only meaningful between a RequestHeaders and RequestBody message. A brief comment noting the Envoy protocol guarantees that prevent stale state would help future readers.

- Add doc comment on MCPParser struct
- Add comment explaining ext_proc pendingHeaders/pendingDirection lifecycle
- Add body size enforcement in ext_proc (maxBodySize = 1MB) with test
- Clarify README prerequisites: cluster type, namespace label

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Hai Huang <huang195@gmail.com>
@huang195

huang195 commented May 3, 2026

Copy link
Copy Markdown
Member Author

All comments are addressed. Thanks @esnible!

@huang195 huang195 closed this May 3, 2026
@huang195 huang195 reopened this May 3, 2026
@huang195
huang195 merged commit ff1146e into rossoctl:main May 3, 2026
32 checks passed
@huang195
huang195 deleted the feat/mcp-parser-body-access branch May 3, 2026 23:20
huang195 added a commit to huang195/kagenti-extensions that referenced this pull request May 5, 2026
- Add doc comment on MCPParser struct
- Add comment explaining ext_proc pendingHeaders/pendingDirection lifecycle
- Add body size enforcement in ext_proc (maxBodySize = 1MB) with test
- Clarify README prerequisites: cluster type, namespace label

Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com>
Signed-off-by: Hai Huang <huang195@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants